#ifndef _M_ROUTING_H_
#define _M_ROUTING_H_

#include <iostream>
#include <google/protobuf/map.h>
#include <string>
#include <vector>
#include <cassert>
#include <sstream>
#include <functional>
#include <mutex>

#include "../mqcommon/mqProto.pb.h"
#include "../mqcommon/logger.hpp"
#include "../mqcommon/helper.hpp"

namespace MyRabbitMQ
{
    class Router
    {
    public:
        //是否合法
        static bool isLegalRoutingKey(const std::string& routingKey)
        {
            for(auto& f : routingKey)
            {
                if((f >= '0' && f <= '9') ||
                (f >= 'a' && f <= 'z') ||
                (f >= 'A' && f <= 'Z') ||
                f == '_' || f == '.' )
                    continue;
                else return false;
            }
            return true;
        }

        static bool isLegalBindingKey(const std::string& bindingKey)
        {
            //不能出现非法字符
            for(auto& f : bindingKey)
            {
                if((f >= '0' && f <= '9') ||
                (f >= 'a' && f <= 'z') ||
                (f >= 'A' && f <= 'Z') ||
                f == '_' || f == '.' || f == '*' || f == '#' )
                    continue;
                else return false;
            }

            // * 和 # 必须独立存在
            std::vector<std::string> sub_words;

            StringHelper::split(bindingKey,".",&sub_words);

            for(auto& f : sub_words)
            {
                if(f.size() > 1 && 
                ((f.find('*') != std::string::npos) || (f.find('#') != std::string::npos)))
                {
                    return false;
                }
            }

            // * 和 # 不能连续出现
            for(int i = 1; i < sub_words.size();++i)
            {
                //** 可以 *# #* ##不能出现
                if(sub_words[i] == "#" && sub_words[i-1] == "#") return false;
                if(sub_words[i] == "#" && sub_words[i-1] == "*") return false;
                if(sub_words[i] == "*" && sub_words[i-1] == "#") return false;
            }

            return true;
        }

        static bool route(protoMQ::ExchangeType type,const std::string& routingKey,const std::string& bindingKey)
        {
            if(!isLegalBindingKey(bindingKey))
            {
                LOG(logLevel::WARNING) << "binding key 不合法!" << std::endl;
                return false;
            }

            if(!isLegalRoutingKey(routingKey))
            {
                LOG(logLevel::WARNING) << "routing key 不合法!" << std::endl;
                return false;
            }

            //合法. 根据交换机类型进行匹配
            if (type == protoMQ::ExchangeType::direct)
                return routingKey == bindingKey;

            if (type == protoMQ::ExchangeType::fanout)
                return true;

            if (type == protoMQ::ExchangeType::unknowType)
            {
                LOG(logLevel::ERROR) << "未知交换机类型, 路由匹配失败" << std::endl;
                return false; 
            }

            //主题交换
            std::vector<std::string> vt_routing;
            size_t rsize = StringHelper::split(routingKey,".",&vt_routing);

            std::vector<std::string> vt_binding;
            size_t bsize = StringHelper::split(bindingKey,".",&vt_binding);

            //定义二维数组
            std::vector<std::vector<bool>> dp(bsize + 1,std::vector<bool>(rsize + 1,false));

            //初始化dp表
            dp[0][0] = true;

            //起始行为 # 时, 需要额外初始化, 将dp表的第0列全部初始化为true
            if(vt_binding[0] == "#")
            {
                for(int i = 1; i <= bsize;++i)
                {
                    dp[i][0] = true;
                }
            }

            for(int i = 1; i <= bsize; ++i)
            {
                for(int j = 1; j <= rsize;++j)
                {
                    //如果binding key 是 #
                    if(vt_binding[i-1] == "#")
                    {
                        dp[i][j] = dp[i-1][j-1] | dp[i][j-1] | dp[i-1][j];
                    }

                    //如果binding key 相等或为 * 
                    else if(vt_binding[i-1] == "*" || vt_binding[i-1] == vt_routing[j-1])
                    {
                        dp[i][j] = dp[i-1][j-1];
                    }
                }
            }

            return dp[bsize][rsize];
        }

        
    };
};

#endif